use crate::backtest::trade_api::SharedTradeContext;
use crate::backtest::TradeRequest;
use crate::db::kline;
use crate::db::trade::trade_bot_with;
use crate::db::trade::trade_order;
use crate::model::trade::trade_bot_with::TradeBot;
use anyhow::anyhow;
use anyhow::Result;

use super::trade_engine::TradeEngine;

/// Back testing system main entry point
pub async fn start_trade_bot_backtest(trade_engine: &mut TradeEngine) -> Result<Vec<TradeRequest>> {
    let trade_bot_id = &trade_engine.get_trade_bot_id();
    let trade_bot: TradeBot = trade_bot_with::find(trade_bot_id).await?;

    println!(
        "(back_test::start_trade_bot_backtest) trade_bot_id= {} trade_bot= {:?}",
        trade_bot_id,
        trade_bot
    );
    if let None = trade_bot.security_id {
        return Err(anyhow!("trade_bot.security_id is None"));
    }
    if let None = trade_bot.klt {
        return Err(anyhow!("trade_bot.klt is None"));
    }
    if let None = trade_bot.script {
        return Err(anyhow!("trade_bot.script is None"));
    }

    // if let None = trade_bot.script_verified {
    //     return Err(anyhow!("trade_bot.script_verified is None"));
    // }
    // if let Some(script_verified) = trade_bot.script_verified {
    //     if !script_verified {
    //         return Err(anyhow!("trade_bot.script_verified is false"));
    //     }
    // }
    const PAGE_SIZE: u16 = 100;
    if
        let TradeBot {
            security_id: Some(ref security_id),
            klt: Some(ref klt),
            script: Some(ref script),
            kl_window,
            ..
        } = trade_bot
    {
        // do backtest
        let _kl_window = match kl_window {
            Some(v) => v,
            None => 1,
        };
        println!(
            "(back_test::start_trade_bot_backtest) security_id={},klt={},script={}, kl_window={:?} _kl_window={}",
            security_id,
            klt,
            script,
            kl_window,
            _kl_window
        );

        let security_id_str = security_id.id.to_raw();
        // clean all the trade_orders created by this trade_bot
        let _ = trade_order::delete_all_by(trade_bot_id).await?;
        let mut no_more_klines = false;
        let mut pi = 0;
        while !no_more_klines {
            // select all the history klines of this trade_bot
            let klines = kline::select(
                &security_id_str,
                None,
                Some(&klt),
                Some(" klts ASC"),
                pi,
                PAGE_SIZE
            ).await?;

            for (index, kline) in klines.iter().enumerate() {
                trade_engine.next_kline(kline);
                let rs = trade_engine.run_script(&script);
                println!(
                    "pi={} kline[{:02 }]= {:?},{},{:.2} trade_engine rs={:?}",
                    pi,
                    index,
                    kline.id,
                    kline.klt,
                    kline.klts,
                    rs
                );
            }
            if klines.len() < (PAGE_SIZE as usize) {
                no_more_klines = true;
            }

            if klines.len() < (PAGE_SIZE as usize) {
                no_more_klines = true;
            }
            pi += 1;
        }
    } else {
        return Err(
            anyhow!(
                "trade_bot's values missing, trade_bot_id={} trade_bot={:?}",
                trade_bot_id,
                trade_bot
            )
        );
    }

    // after running all klines on the script, trade_engine.scope.trade_context should hold the trade_requests
    let shared_trade_context: Option<SharedTradeContext> = trade_engine.scope.get_value("Tapi");
    if let Some(shared_trade_context) = shared_trade_context {
        let trade_context = shared_trade_context.lock().unwrap();
        println!(
            "\n(back_test::start_trade_bot_backtest) trade_context.trade_request_queue.len= {:?}",
            trade_context.trade_request_queue.lock().unwrap().len()
        );

        // trade_context
        //     .trade_request_queue
        //     .lock()
        //     .unwrap()
        //     .iter()
        //     .for_each(|trade_request| {
        //         println!(
        //             "(back_test::start_trade_bot_backtest) trade_request= {:?}",
        //             trade_request
        //         );
        //     });

        let mut guard = trade_context.trade_request_queue.lock().unwrap();

        let trade_requests: Vec<TradeRequest> = guard.drain(..).collect();

        let trade_requests_copy = trade_requests.clone();
        let trade_bot_id_copy = trade_context.trade_bot_id.clone();
        // Spawn a task that performs a long-running operation: convert TradeRequest to TradeOrders and then save TradeOrders to DB .
        tokio::spawn(async move {
            println!("(back_test::start_trade_bot_backtest) Starting long running operation...");
            println!(
                "(back_test::start_trade_bot_backtest) convert TradeRequest to TradeOrders and then save TradeOrders to DB..."
            );

            let _ = trade_order::delete_all_by(&trade_bot_id_copy).await;
            for (idx, trade_request) in trade_requests_copy.iter().enumerate() {
                println!(
                    "(back_test::start_trade_bot_backtest) idx={}, trade_request={:?}",
                    idx,
                    trade_request
                );

                let rs = trade_order::handle_trade_request(trade_request).await;

                println!("(back_test::start_trade_bot_backtest) handle_trade_request.rs={:?}", rs);
            }
            println!("trade_requests.len={}", trade_requests_copy.len());
            tokio::time::sleep(std::time::Duration::from_secs(5)).await;
            println!("(back_test::start_trade_bot_backtest) Long running operation finished.");
        });

        return Ok(trade_requests);
    }

    Ok([].to_vec())
}

#[cfg(test)]
mod tests {
    use crate::backtest::trade_engine::TradeEngine;
    use crate::db;

    #[tokio::test]
    async fn test_start_trade_bot_backtest() {
        let _ = db::init().await.unwrap();

        let mut trade_engine: TradeEngine = TradeEngine::new("o2ld1z53ziniwzaccoyx", 3);

        // test/test> select * from trade_bot_with where in=security:⟨SZ.KZZ.128136⟩ AND out.klt='DAY'
        // [[{ id: trade_bot_with:j9tpv2dyh3op2jwgr107, in: security:⟨SZ.KZZ.128136⟩, is_running: false, last_updated: '2024-06-06 12:59:33.859683 UTC', out: trade_strategy:⟨Tstg.DAY.0000⟩ }]]

        let res = super::start_trade_bot_backtest(&mut trade_engine).await;
        println!("(back_test::test_start_trade_bot_backtest) res={:?}", res);

        tokio::time::sleep(std::time::Duration::from_secs(15)).await;
    }
}
